import * as THREE from "../../../modules/three/three.module.js";
import {d2r, mapCodeToCssColor, mapColorCssToCode, WorldConfig} from "../lib/CommonConsts.js";
import FileSaver from "../../../../lib/FileSaver.js"
import {
  areaMtl,
  bufferAttributeToVectors,
  flatten,
  getPolygonGeometry,
  getMeshRect,
  holeMtl,
  degYToCoord, getYAngleDeg
} from "../lib/util/StaticMethods.js";
import WorkArea from "./WorkArea.js";
import HoleMesh from "../lib/gadget/HoleMesh.js";
import WorkAreaMesh from "../lib/gadget/WorkAreaMesh.js";
import World3dEvent from "../scene/event/World3dEvent.js";
import SprayPerformer from "../lib/gadget/SprayPerformer.js";
import Cartesian3 from "../lib/cesium/Core/Cartesian3.js";
import Ellipsoid from "../lib/cesium/Core/Ellipsoid.js";
import DroneIconPoint from "../lib/gadget/DroneIconPoint.js";
import Blocking from "./Blocking.js";
import BlockingMesh from "../lib/gadget/BlockingMesh.js";
import UndoType from "../scene/undo/UndoType.js";
import {arrAsubB, removeArrayItem} from "../lib/util/ToolMethods.js";
import PathCompute from "../pathCompute/PathCompute.js";
import PathComputeUI from "../pathCompute/PathComputeUI.js";
import PlaneRefVO from "./PlaneRefVO.js";
import PlaneRefMesh from "../lib/gadget/PlaneRefMesh.js";
import ConvexAreaVO from "./ConvexAreaVO.js";
import ConvexAreaMesh from "../lib/gadget/ConvexAreaMesh.js";
import {hideSprayPointTip, showAddingWorkAreaTip, showSprayPointTip} from "../scene/MeasureUtil.js";
import SprayPathIndicator from "../lib/gadget/SprayPathIndicator.js";
import RouteVO from "./RouteVO.js";
import RoutePointIndicator from "../lib/gadget/RoutePointIndicator.js";
import NormalPointVO from "./NormalPointVO.js";
import NormalPointIndicator from "../lib/gadget/NormalPointIndicator.js";
import IsoLineVO from "./IsoLineVO.js";
import IsoLineEntity from "../lib/gadget/IsoLineEntity.js";
import ar from "../../../../lib/element-plus@2.8.4/index.full.min.mjs";

// 状态 bool 变量
// 正在设置相机焦点的状态
const stateSettingFocus = 'stateSettingFocus';
// 在模型上设置校准点
const stateSettingCalibrationPoint = 'stateSettingCalibrationPoint';
// 在模型上设置航线起飞点，目前只能设置一个航线起飞点
const stateSettingHomePoint = 'stateSettingHomePoint';
// 添加工作区状态
const stateAddingArea = 'stateAddingArea';
// 添加不可喷涂区域状态
const stateAddingHole = 'stateAddingHole';
// 编辑不可喷涂区域状态
const stateEditingHole = 'stateEditingHole';
// 添加固定视角状态
const stateAddingFixedEye = 'stateAddingFixedEye';
// 为采样空间添加顶点的状态
const stateAddingSampleCubeVert = 'stateAddingSampleCubeVert';
// 开始标定障碍物
const stateAddingBlocking = 'stateAddingBlocking';
// 编辑喷涂航线状态
const stateEditingSprayPath = 'stateEditingSprayPath';
// 拾取模型上任意一点的 gis 信息 （经纬度，高程，投影坐标）
const stateMeasurePoint = 'stateMeasurePoint';
// 在墨卡托二维投影地图上,拾取平面任意一点的 经纬度
const statePickLngLat = 'statePickLngLat';
// 设置 三维避障寻路的起始点 和 终点
const stateMovingStartEnd = 'stateMovingStartEnd';
// 设置 工作区辅助平面
const stateAddingPlaneRef = 'stateAddingPlaneRef';
// 添加切割线段, 支持在不规则曲面上标记工作区
const stateAddingSliceLine = 'stateAddingSliceLine';
// 添加支持曲面的喷涂航点，
const stateAddingRoutePoint = 'stateAddingRoutePoint';
// 移动喷涂航点，
const stateMovingRoutePoint = 'stateMovingRoutePoint';
// 添加等值线，可能新增法向采样点（局部切平面）
const stateAddingNormalPoint = 'stateAddingNormalPoint';
// 移动法向采样点，
const stateMovingNormalPoint = 'stateMovingNormalPoint';
// 为采样三维凸包网格 添加顶点的状态
const stateAddingConvexVert = 'stateAddingConvexVert';
// 为曲面网格 添加顶点的状态
const stateAddingGridVert = 'stateAddingGridVert';
// 为曲面网格 添加局部的切平面工作区
const stateAddingSurfaceArea = 'stateAddingSurfaceArea';

// 无状态
const stateFree = "stateFree"
let state = stateFree

const origin = new THREE.Vector3(0,0,0)
const tmp = new THREE.Vector3(0,0,0)
const tmp2 = new THREE.Vector3(0,0,0)
const tmpCartesian3 = new Cartesian3();
const tmpQuat = new THREE.Quaternion();
const unit_x = new THREE.Vector3(1,0,0);
const axes4PlaneRef = new THREE.AxesHelper(3)

const facing = {
  // 从相机焦点指向相机当前位置
  vector: new THREE.Vector3(),
  // 相机朝向的几何平面
  plane: new THREE.Plane(),
  // 平面内坐标 * planeMat == 世界坐标
  planeMat: new THREE.Matrix4(),
  planeMat0: new THREE.Matrix4(),
  // 世界坐标 * planeMatInv == 平面内坐标
  planeMatInv: new THREE.Matrix4(),
  planeMatInv0: new THREE.Matrix4(),

  updateByAreaOrHoleMesh(areaMesh) {
    const workArea = areaMesh.vo
    facing.vector.copy(workArea.facing)
    facing.plane.copy(workArea.createPlane())
    facing.planeMat.copy(workArea.planeMat)
    facing.planeMatInv.copy(workArea.planeMatInv)
  },
  updateByEye(camera, cameraTargetPosition, intersectPoint, span) {
    facing.vector.copy(camera.position).sub(cameraTargetPosition).normalize()
    facing.plane.setFromNormalAndCoplanarPoint(facing.vector, intersectPoint)
    const dist = facing.plane.constant - span
    facing.plane.constant = dist
    const offset = new THREE.Vector3().copy(facing.vector).multiplyScalar(-dist)
    const translateMat = new THREE.Matrix4().makeTranslation(offset.x, offset.y, offset.z)
    let rotateMat = new THREE.Matrix4().lookAt(origin, facing.vector, camera.up)
    facing.planeMat.multiplyMatrices(translateMat, rotateMat)
    facing.planeMatInv.copy(facing.planeMat).invert()
  },

  updateByPlaneRef(camera, planeRefMesh, alignToEye) {
    facing.vector.copy(planeRefMesh.vo.plane.normal)
    const dir = camera.position.clone().sub(planeRefMesh.vo.pos)
    if (dir.dot(facing.vector) < 0) {
      facing.vector.negate()
    }
    const [plane0, planeMat0] = planeRefMesh.vo.createPlaneAndMat()
    const offset = new THREE.Vector3().setFromMatrixPosition(planeMat0)
    if (!alignToEye) {
      facing.plane.copy(plane0)
      const [x, y, z] = planeRefMesh.vo.rot
      const euler = new THREE.Euler(x, y, z)
      facing.planeMat.makeRotationFromEuler(euler)
      facing.planeMat.setPosition(offset.x, offset.y, offset.z)
    } else {
      const raycaster2 = new THREE.Raycaster()
      raycaster2.setFromCamera({x: 0, y: 0.1}, camera)
      const ptO = new THREE.Vector3()
      const ptY = new THREE.Vector3()
      raycaster2.ray.intersectPlane(facing.plane, ptY)
      raycaster2.setFromCamera({x: 0, y: 0}, camera)
      raycaster2.ray.intersectPlane(facing.plane, ptO)
      const uy = ptY.clone().sub(ptO).normalize()
      const uz = facing.plane.normal
      const ux = uy.clone().cross(uz)
      uy.copy(uz).cross(ux).normalize()
      facing.planeMat.makeBasis(ux, uy, uz)
      facing.planeMat.setPosition(offset.x, offset.y, offset.z)
    }
    facing.planeMatInv.copy(facing.planeMat).invert()

    facing.planeMat0.copy(planeMat0)
    facing.planeMatInv0.copy(planeMat0).invert()
  },
}

const rectOnPlane = {
  // 鼠标射线和 朝向平面 撞击出的世界坐标，鼠标按下时设置
  startPointWorld: new THREE.Vector3(),
  // startPointWorld在当前朝向平面内的坐标
  startPointLocal: new THREE.Vector3(),
  // 鼠标射线和 朝向平面 撞击出的世界坐标，鼠标移动时设置
  endPointWorld: new THREE.Vector3(),
  // endPointWorld在当前朝向平面内的坐标
  endPointLocal: new THREE.Vector3(),
  // 矩形面积 单位为平方米
  area: 0,
  width: 0,
  height: 0,

  v1: new THREE.Vector3(),
  v3: new THREE.Vector3(),
  uv0: new THREE.Vector2(),
  uv1: new THREE.Vector2(),
  uv2: new THREE.Vector2(),
  uv3: new THREE.Vector2(),
  vectors: [],
  uvs: [],

  /**
   * @param {THREE.Ray} ray
   * @param {THREE.Plane} plane
   * @param {THREE.Matrix4} planeMatInv
   */
  onMouseDown(ray, plane, planeMatInv) {
    ray.intersectPlane(plane, this.startPointWorld)
    this.startPointLocal.copy(this.startPointWorld).applyMatrix4(planeMatInv)
    this.vectors = [this.startPointWorld, this.v1, this.endPointWorld, this.v3]
    this.uvs = [this.uv0, this.uv1, this.uv2, this.uv3]
  },

  /**
   * @param {THREE.Ray} ray
   * @param {THREE.Plane} plane
   * @param {THREE.Matrix4} planeMatInv
   * @param {THREE.Matrix4} planeMat
   */
  onMouseMove(ray, plane, planeMatInv, planeMat) {
    ray.intersectPlane(plane, this.endPointWorld)
    this.endPointLocal.copy(this.endPointWorld).applyMatrix4(planeMatInv)
    this._computeRect(planeMat)
  },

  _computeRect(planeMat) {
    const {v1, v3, uv0, uv1, uv2, uv3} = this
    v1.copy(this.startPointLocal)
    v3.copy(this.endPointLocal)
    v1.x = this.endPointLocal.x
    v3.x = this.startPointLocal.x
    uv0.copy(this.startPointLocal)
    uv2.copy(this.endPointLocal)
    uv1.copy(v1)
    uv3.copy(v3)
    v1.applyMatrix4(planeMat)
    v3.applyMatrix4(planeMat)
    const a = this.uv0.distanceTo(this.uv1)
    const b = this.uv2.distanceTo(this.uv1)
    this.width = b;
    this.height = a;
    this.area = a * b
  }
}

let eyeIndicatorsHided = false;

function toggleVisibility(displayObjects) {
  const objects = displayObjects;
  if (!objects.length) {
    return;
  }
  const visibility = objects[0].visible;
  displayObjects.forEach(obj => obj.visible = !visibility);
}

/**
 * 维护喷涂方案的基础数据，包含固定视角，工作区，不可喷涂区，当前相机的朝向平面，选中的工作区，选中的不可喷涂区
 * 和 当前的编辑状态；
 * 方案的 projectId
 */
export default class MainStore {
  /**
   * 用于展示无人机的实时位置 和 朝向 DroneIconPoint
   * @type {DroneIconPoint}
   * @private
   */
  _droneMesh
  /**
   * 用于航线校准的偏移
   * @type {THREE.Group}
   * @private
   */
  _droneHolder = new THREE.Group()
  _calibrationDiff = {lng: 0, lat: 0, alt: 0}
  /**
   * 项目中心点，userData = { tipPos: string }
   * @type {THREE.Sprite[]}
   */
  locationSprites = [];
  /**
   * 无人机在2d地图 显示的图标 userData = {sn: string, name: string, lng: number, lat: number}
   * @type {THREE.Sprite[]}
   */
  droneSprites = [];
  _locationSpriteVisible = false;
  /**
   * 已经创建好的工作区 和 曲面的局部切平面
   * @type {WorkAreaMesh[]}
   */
  areaMeshes = []
  /**
   * 已经创建好的曲面工作区
   * @type {ConvexAreaMesh[]}
   */
  surfacesAreaMeshes = []
  /**
   * 已经创建好的不可喷涂区
   * @type {HoleMesh[]}
   */
  holeMeshes = []
  /**
   * 已经标定好的障碍物
   * @type {BlockingMesh[]}
   */
  blockings = [];
  /**
   * 已经标定好的航点
   * @type {RoutePointIndicator[]}
   */
  routePoints = [];
  /**
   * 已经标定好的航线
   * @type {RouteVO[]}
   */
  routeList = [];
  /**
   * 正在编辑的航线
   * @type {RouteVO|null}
   */
  currentRoute
  /**
   * 当前选中的航线
   * @type {RouteVO|null}
   */
  pickingRoute
  /**
   * 标定好的工作区辅助平面
   * @type {PlaneRefMesh[]}
   */
  planeRefMeshes = [];
  /**
   * 固定视角列表
   * @type {EyeIndicator[]}
   */
  fixedEyeIndicators = []
  /**
   * AI 规划出来航线的显示器，用于动画
   * @type {SprayPathIndicator[]}
   */
  sprayPathIndicators = []
  /**
   * AI 规划出来航线的显示器，传给无人机飞控
   * @type {FlyPathIndicatorV2[]}
   */
  flyPathIndicators = [];
  /**
   * 校准点 图标列表
   * @type {THREE.Sprite[]}
   */
  calibrationPoints = [];
  /**
   * 航线起飞点 图标列表
   * @type {THREE.Sprite[]}
   */
  flyHomePoints = [];
  /**
   * 当前选中的校准点，鼠标移动时，位于图标上面，就算选中图标
   * @type {THREE.Sprite|null}
   */
  curHoverPoint;
  /**
   * @type {null|PlaneRefMesh}
   */
  usingPlaneRef = null;
  // true 表示当激活倾斜辅助平面，或工作区时，允许创建与屏幕视口平齐的矩形工作区
  _alignToViewport = false

  _intersection
  /**
   * 当前拾取的对象
   * 可能为 固定视角 | 不可喷涂区网格 | 工作区网格 | b3dm模型的一个瓦片网格 | 无
   * @private
   * @type {EyeIndicator | HoleMesh | Mesh | Tile | null}
   */
  _intersectObj
  /**
   * 当前正在使用的视角
   * @type {EyeIndicator}
   */
  _usingFixedEye

  // 正在新增的工作区网格
  addingAreaMesh
  // 正在新增的不可喷涂区网格
  addingHoleMesh

  world3d

  constructor(world3d) {
    this.world3d = world3d
    // this._droneMesh = new SprayPerformer();
    // this._droneMesh.toggleLine(false);
    this._droneMesh = new DroneIconPoint();
    this._droneHolder.add(this._droneMesh);
    world3d.scene.add(this._droneHolder);
    this._droneMesh.visible = false;

    // const droneIcon = new DroneIconPoint();
    // world3d.scene.add(droneIcon)
  }
  setAlignToViewport(val) {
    this._alignToViewport = val
  }
  getAlignToViewport() {
    return this._alignToViewport
  }
  getFacing() {
    return facing
  }

  // 重置3d场景 和 数据中心保存的数据
  clearScene() {
    this.areaMeshes.forEach((it) => it.removeFromParent());
    this.holeMeshes.forEach((it) => it.removeFromParent());
    // this.fixedEyeIndicators.forEach((it) => it.removeFromParent());
    this.sprayPathIndicators.forEach((it) => it.removeFromParent());
    this.flyPathIndicators.forEach((it) => it.removeFromParent());
    this.calibrationPoints.forEach((it) => it.removeFromParent());
    this.flyHomePoints.forEach((it) => it.removeFromParent());
    this.blockings.forEach((it) => it.removeFromParent());
    this.planeRefMeshes.forEach((it) => it.removeFromParent());
    this.surfacesAreaMeshes.forEach((it) => it.removeFromParent());
    this.routePoints.forEach((it) => {
      it.removeFromParent()
      it.disposeLine()
    });
    this.areaMeshes.length = 0;
    this.holeMeshes.length = 0;
    // this.fixedEyeIndicators.length = 0;
    this.sprayPathIndicators.length = 0;
    this.flyPathIndicators.length = 0;
    this.calibrationPoints.length = 0;
    this.flyHomePoints.length = 0;
    this.blockings.length = 0;
    this.planeRefMeshes.length = 0;
    this.surfacesAreaMeshes.length = 0;
    // this.hideDroneMesh();
    this.routePoints.length = 0;
    this.routeList.length = 0;
    this.currentRoute = this.pickingRoute = null
  }

  clearFlyPathIndicators() {
    this.flyPathIndicators.forEach((it) => it.removeFromParent());
    this.flyPathIndicators.length = 0;
  }

  /**
   * 接收地面站的websocket消息，进行航线校准
   * @param msgData
   */
  setCalibrationDiff(msgData) {
    this._calibrationDiff = msgData;
  }

  /**
   * 把当前的编辑数据保存为Json对象
   * @return {}
   */
  storageSave() {
    this.world3d._selectAreaPlugin.detachItem();
    const target = [];
    const holes = [];
    this.holeMeshes.concat(this.areaMeshes).forEach((mesh) => {
      const workArea = mesh.vo;
      workArea.computeArea();
      workArea.setColorByMesh(mesh)
      const jObj = workArea.toJsonObj()
      if (this.areaMeshes.includes(mesh)) {
        target.push(jObj);
      } else {
        holes.push(jObj);
      }
    });
    const areaObj = { list: target, holes };
    this.blockings.forEach((it) => it.updateVO());
    const blockingObjs = this.blockings.map((it) => it.vo.toJsonObj());
    const planeRefObjs = this.planeRefMeshes.map((it) => it.vo.toJsonObj());
    if (this.blockings.length) {
      // 计算每个工作区的喷涂死角
      const blockingVOs = this.blockings.map(it => it.vo);
      const areas = this.areaMeshes.map(it => it.vo);
      const pathCompute = new PathCompute(blockingVOs, areas);
      this.areaMeshes.forEach((areaMesh) => {
        const cornerResult = pathCompute.findCornersAndSprayAreas(
          areaMesh.vo, null
        );
        if (cornerResult.corners.length) {
          const areaObj = target.find((it) => it.id === areaMesh.vo.id);
          areaObj.corners = cornerResult.corners;
          areaObj.subAreas = cornerResult.sprayAreas;
          PathComputeUI.displayCornersAndAreas(this.world3d.scene, areaMesh, cornerResult);
        }
      });
    }

    const eye = this.world3d.camera.position;
    const focus = this.world3d.cameraControls.target;
    const orientations = {
      currentEye: { x: eye.x, y: eye.y, z: eye.z },
      currentFocus: { x: focus.x, y: focus.y, z: focus.z },
      fixedEyes: this.fixedEyeIndicators.map((it) => {
        return {
          eye: { x: it.position.x, y: it.position.y, z: it.position.z },
          focus: { x: it.focus.x, y: it.focus.y, z: it.focus.z },
        };
      }),
    };
    // 保存若干个航线校准点
    const calibrationPoints = this.calibrationPoints.map((it) =>
      it.position.toArray(),
    );
    const flyHomeSprites = this.flyHomePoints.map((it) =>
      it.position.toArray(),
    );
    // 若干个航线校准点 的经纬高，给地面站使用
    const calibrationGeoCoords = this.calibrationPoints.map(
      (it) => it.userData.coord,
    );
    const flyHomePoints = this.flyHomePoints.map((it) => it.userData.coord);
    const surfaceMeshes = [];
    this.surfacesAreaMeshes.forEach(mesh => {
      mesh.updateVO();
      surfaceMeshes.push(mesh.vo.toJsonObj());
    });
    const routes = []
    this.getRouteList().forEach(route => {
      if (route.pointMeshes.length) {
        routes.push(route.toJsonObj())
      }
    })
    const obj = {
      eyes: orientations,
      workAreas: areaObj,
      blockings: blockingObjs,
      planeRefs: planeRefObjs,
      globalClip: {},
      calibrationPoints,
      calibrationGeoCoords,
      flyHomeSprites,
      flyHomePoints,
      surfaceMeshes,
      routes,
      version: WorldConfig.version,
    };
    let customPath;
    const pathPointPlugin = this.world3d._addSprayPathPointPlugin;
    if (pathPointPlugin.sprayPath) {
      customPath = pathPointPlugin.storageSave();
      obj.customPath = customPath;
    }
    obj.pathEidtMap = {};
    pathPointPlugin.recorderMap.forEach((value, key) => {
      obj.pathEidtMap[key] = value;
    });
    this.world3d._addIsoLinePlugin.storageSave(obj)
    this.world3d._surfaceGridPlugin.storageSave(obj)
    return obj;
  }

  /**
   * 加载json文件得到的对象
   * @param {object} obj
   */
  storageLoad(obj) {
    if (!obj) {
      return
    }
    this.clearScene()
    if (obj.toEarchCoordMat) {
      this.world3d._meta.toEarchCoordMat.fromArray(obj.toEarchCoordMat)
      this.world3d._meta.toEarchCoordMatInv.fromArray(obj.toEarchCoordMat).invert()
    }
    const workAreas = obj.workAreas.list
    const holeAreas = obj.workAreas.holes
    if (workAreas) {
      this.areaMeshes.length = 0
      workAreas.forEach(obj => {
        const workArea = new WorkArea().fromJsonObj(obj)
        const areaMesh = new WorkAreaMesh().setFromVO(workArea)
        this.areaMeshes.push(areaMesh)
        this.world3d.scene.add(areaMesh)

        if (obj.corners) {
          areaMesh.userData.corners = obj.corners;
          areaMesh.userData.subAreas = obj.subAreas;
          PathComputeUI.displayCornersAndAreas(this.world3d.scene, areaMesh);
        }
      })
    }
    if (holeAreas) {
      this.holeMeshes.length = 0
      holeAreas.forEach(obj => {
        const vo = new WorkArea().fromJsonObj(obj)
        const holeAreaMesh = new HoleMesh().setFromVO(vo)
        this.holeMeshes.push(holeAreaMesh)
        this.world3d.scene.add(holeAreaMesh)
      })
    }
    if (obj.calibrationPoints && obj.calibrationPoints.length) {
      // 加载若干个航线校准点
      obj.calibrationPoints.forEach((array) => {
        const position = new THREE.Vector3().fromArray(array);
        this.world3d.addCalibrationPoint(position);
      });
    }
    if (obj.flyHomeSprites && obj.flyHomeSprites.length) {
      // 加载若干个航线起飞点，调用时机：3d tiles 的部分瓦片已经绘制出来了
      obj.flyHomeSprites.forEach((array) => {
        const position = new THREE.Vector3().fromArray(array);
        this.world3d.addOrUpdateFlyHomePoint(position);
      });
    }

    if (obj.globalClip) {
      WorldConfig.globalClipping = obj.globalClip
    }

    const { currentEye, currentFocus, fixedEyes } = obj.eyes;
    this.world3d.camera.position.copy(currentEye);
    this.world3d.setCameraFocus(currentFocus);
    this.world3d.cameraControls.update();
    fixedEyes.forEach((it) => {
      this.world3d.editOrientation.addEyeIndicator(it.eye, it.focus);
    });
    this.world3d.editOrientation.updateEyeIndicatorsVisible();
    const pathPointPlugin = this.world3d._addSprayPathPointPlugin
    if (obj.customPath && obj.customPath.wallPath) {
      pathPointPlugin.storageLoad(obj.customPath)
    }
    if (obj.pathEidtMap) {
      Object.keys(obj.pathEidtMap).forEach(key => {
        pathPointPlugin.recorderMap.set(key, obj.pathEidtMap[key])
      })
    }
    this.world3d.loadSprayPathsFromLocal();
    if (obj.blockings) {
      obj.blockings.forEach((jObj) => {
        const vo = new Blocking().fromJsonObj(jObj);
        const blockingMesh = new BlockingMesh().initBlocking(vo);
        this.blockings.push(blockingMesh);
        this.world3d.scene.add(blockingMesh);
      });
    }
    if (obj.planeRefs) {
      obj.planeRefs.forEach((jObj) => {
        const vo = new PlaneRefVO().fromJsonObj(jObj);
        const planeRefMesh = new PlaneRefMesh().initPlaneRef(vo);
        this.planeRefMeshes.push(planeRefMesh);
        this.world3d.scene.add(planeRefMesh);
      });
    }
    if (obj.surfaceMeshes) {
      obj.surfaceMeshes.forEach(jObj => {
        const vo = new ConvexAreaVO().fromJsonObj(jObj);
        const convexMesh = new ConvexAreaMesh().initByVO(vo);
        this.surfacesAreaMeshes.push(convexMesh);
        this.world3d.scene.add(convexMesh);
      });
      this.world3d.editOrientation.shapeExtractAdhere.onAreaAdded();
    }
    if (obj.routes) {
      obj.routes.forEach(routeJson => {
        const vo = new RouteVO().fromJsonObj(routeJson)
        this.routeList.push(vo)
        const lastIndex = vo.pointsByJson.length - 1
        vo.pointsByJson.forEach((routePoint, index) => {
          const indicator = new RoutePointIndicator(routePoint, this.world3d.scene)
          vo.pointMeshes.push(indicator)
          this.routePoints.push(indicator)
          this.world3d.scene.add(indicator)
          if (index === 0) {
            indicator.markAsStart()
          } else if (index === lastIndex) {
            indicator.markAsEnd()
          }
        })
        vo.pointMeshes.forEach((pointMesh, index) => {
          if (index > 0) {
            const pre = vo.pointMeshes[index - 1].vo
            pointMesh.createLine(this.world3d.scene, pre)
          }
        })
      })
    }
    this.world3d._addIsoLinePlugin.storageLoad(obj)
    this.world3d._surfaceGridPlugin.storageLoad(obj)
  }

  // 导出辅助平面
  exportPlaneRefs() {
    if (!this.planeRefMeshes.length) {
      return;
    }
    const mUrl = this.world3d._modelUrl;
    const planeRefs = this.planeRefMeshes.map((it) => it.vo.toJsonObj());
    const jObj = { url: mUrl, planeRefs };
    const index = mUrl.lastIndexOf('upload');
    const nameDefault = mUrl.substring(index + 7);
    let name = prompt('请输入文件名称', nameDefault);
    if (name && name.trim()) {
      let content = JSON.stringify(jObj);
      let blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
      FileSaver.saveAs(blob, name + '.json');
    }
  }

  importPlaneRefs(file) {
    if (!file) {
      return;
    }
    const extension = file.name.split('.').pop().toLowerCase();
    if (extension === 'json') {
      const reader = new FileReader();
      reader.readAsText(file, 'charset=utf-8');
      reader.addEventListener('load', (evt) => {
        const contents = evt.target.result;
        const jsonObj = JSON.parse(contents);
        if (jsonObj.url !== this.world3d._modelUrl) {
          if (confirm('导入的平面辅助数据与当前模型不一致')) {
            this.loadPlaneRefs(jsonObj);
          }
        } else {
          this.loadPlaneRefs(jsonObj);
        }
      });
    }
  }

  loadPlaneRefs(jsonObj) {
    this.planeRefMeshes.forEach((it) => it.removeFromParent());
    this.planeRefMeshes.length = 0;
    jsonObj.planeRefs.forEach((jObj) => {
      const vo = new PlaneRefVO().fromJsonObj(jObj);
      const planeRefMesh = new PlaneRefMesh().initPlaneRef(vo);
      this.planeRefMeshes.push(planeRefMesh);
      this.world3d.scene.add(planeRefMesh);
    });
  }

  _makeCameraFocusWorkAreas(meshes) {
    let vectors = []
    meshes.forEach(mesh => {
      const points = bufferAttributeToVectors(mesh.geometry.getAttribute('position'))
      vectors = vectors.concat(points)
    })
    const box3 = new THREE.Box3().setFromPoints(vectors)
    const orient = meshes[0].vo.facing.clone()
    orient.add(new THREE.Vector3(0, 0, 1)).normalize().negate()
    const focus = box3.getCenter(new THREE.Vector3())
    const len = box3.getSize(new THREE.Vector3()).length()
    this.world3d.onCameraTargetChange(orient, focus, len * 2)
  }

  focusDroneMesh() {
    if (!this._droneMesh.visible) {
      return;
    }
    // this._droneHolder.updateMatrixWorld()
    // tmp2.setFromMatrixPosition(this._droneMesh.matrixWorld);
    tmp.copy(unit_x).applyQuaternion(this._droneMesh.quaternion);
    this.world3d.onCameraTargetChange(tmp, this._droneMesh.position, 10);
  }

  showDroneMesh(lng, lat, altitude, yaw) {
    this._droneMesh.visible = true;
    if (isNaN(parseFloat(lng)) || isNaN(parseFloat(lat))) {
      return;
    }
    const diff = this._calibrationDiff;
    const lngCalibrated = lng + diff.lng;
    const latCalibrated = lat + diff.lat;
    const altCalibrated = altitude + diff.alt;
    const earthCoord = Cartesian3.fromDegrees(lngCalibrated, latCalibrated, altCalibrated, Ellipsoid.WGS84, tmpCartesian3);
    tmp.set(earthCoord.x, earthCoord.y, earthCoord.z).applyMatrix4(this.world3d._meta.toEarchCoordMatInv);
    this._droneMesh.position.copy(tmp);
    const [x, y] = degYToCoord(yaw);
    tmp.set(x, y, 0);
    this._droneMesh.quaternion.setFromUnitVectors(unit_x, tmp);
    const flyPathIndicator = this.flyPathIndicators[0];
    if (flyPathIndicator) {
      flyPathIndicator.updateByDroneRealPosition(lngCalibrated, latCalibrated, altCalibrated);
    }
  }

  updateFlyPathProgress(index) {
    const flyPathIndicator = this.flyPathIndicators[0];
    if (flyPathIndicator) {
      flyPathIndicator.updateFlyProgress(index);
    }
  }

  hideDroneMesh() {
    this._droneMesh.visible = false;
  }

  isDroneMeshShowing() {
    return this._droneMesh.visible;
  }

  // 根据鼠标射线和编辑器的编辑状态，判定当前拾取的对象，
  setIntersectObject(event) {
    hideSprayPointTip();
    if (this.stateEditingHole && this.world3d.editingVertAndAhering()) {
      // 正在编辑孔洞，并且鼠标已经吸附到顶点或边，这时不允许选中 其他孔洞 或 工作区
      return this._intersectObj;
    }
    const markingRag = this.world3d.editOrientation.shapeExtract.isMarkingRag();
    if (
      this.stateEditingSprayPath ||
      this.stateMeasurePoint ||
      this.statePickLngLat ||
      this.stateAddingBlocking ||
      this.stateMovingStartEnd ||
      this.stateAddingSliceLine ||
      this.stateAddingPlaneRef ||
      this.stateAddingRoutePoint||
      this.stateMovingRoutePoint||
      this.stateAddingNormalPoint||
      this.stateMovingNormalPoint||
      this.stateAddingConvexVert||
      this.stateAddingGridVert||
      this.stateAddingSurfaceArea||
      markingRag
    ) {
      // 正在测量坐标 或 正在编辑喷涂航线 或 正在标定障碍物 或 正在设置路径起点和终点，不允许选中其他工作区
      return this._intersectObj;
    }
    const eyeMeshes = this.fixedEyeIndicators
      .filter((it) => it.visible)
      .map((it) => it.eyeMesh);
    let meshes = [];
    let needCheckTile = false; // true 表示需要和 b3dms 网格瓦片进行撞击

    switch (state) {
      case stateFree:
        meshes = this.areaMeshes
          .concat(this.planeRefMeshes)
          .concat(this.surfacesAreaMeshes)
          .concat(this.holeMeshes)
          .concat(eyeMeshes)
          .concat(this.sprayPathIndicators);
        break;
      case stateAddingArea:
      case stateAddingHole: // 新增孔洞时，允许选中老孔洞，并删除
      case stateEditingHole:
        meshes.push(...this.areaMeshes);
        meshes.push(...eyeMeshes);
        meshes.push(...this.holeMeshes);
        needCheckTile = true;
        break;
      case stateAddingSampleCubeVert:
      case stateSettingFocus:
      case stateSettingCalibrationPoint:
      case stateSettingHomePoint:
        needCheckTile = true;
        break;
      default: // stateAddingFixedEye
        break;
    }
    if (this.pickingSprayPathIndicator()) {
      this.world3d.outlineHelper.unSelect();
    }
    if (this._mousedown4UsingPlaneRef()) {
      return this._intersectObj;
    }
    this._intersectObj = null;
    this._intersection = null;
    const raycaster = this.world3d.mouseRaycaster;
    const intersects = raycaster.intersectObjects(meshes, false);
    let intersectTile;
    if (needCheckTile) {
      const tileIntersects = raycaster.intersectObject(
        this.world3d.tiles.group,
        true,
      );
      if (tileIntersects.length) {
        intersectTile = tileIntersects[0];
        intersects.push(intersectTile);
      }
    }
    intersects.sort((a, b) => {
      const orderA = a.object.renderOrder
      const orderB = b.object.renderOrder
      if (orderA > 0 && orderB > 0) {
        // 按 renderOrder 由大到小排列
        return orderB - orderA
      }
      return a.distance - b.distance
    });
    this.planeRefMeshes.forEach(it => it !== this.usingPlaneRef && it.toggleSelected(false));
    this.surfacesAreaMeshes.forEach(it => it.toggleSelectArea(false));
    if (intersects.length) {
      this._intersection = intersects[0];
      this._intersectObj = intersects[0].object;
      if (this.pickingPlaneRef()) {
        this.pickingPlaneRef().toggleSelected(true);
      }
      if (this.pickingSurfaceMesh()) {
        this.pickingSurfaceMesh().toggleSelectArea(true);
      }
      if (
        this.stateAddingArea ||
        this.stateAddingHole ||
        this.stateEditingHole
      ) {
        const { camera, cameraControls } = this.world3d;
        let span = 0;
        if (this.stateAddingArea) {
          span = this.pickingArea() ? 0 : WorldConfig.building_plane_distance;
        } else if (this.stateAddingHole) {
          span = this.pickingArea()
            ? WorldConfig.hole_plane_distance
            : WorldConfig.building_plane_distance +
            WorldConfig.hole_plane_distance;
        }
        const areaOrHole = this.pickingArea() || this.pickingHole()
        if (areaOrHole) {
          facing.updateByAreaOrHoleMesh(areaOrHole)
        } else {
          facing.updateByEye(camera, cameraControls.target, intersects[0].point, span)
        }
        rectOnPlane.onMouseDown(raycaster.ray, facing.plane, facing.planeMatInv);
      }
      if (this.pickingSprayPathIndicator()) {
        // console.log('pickingSprayPathIndicator, ', intersects[0])
        const pathIndicator = this.pickingSprayPathIndicator();
        const evt = World3dEvent.sprayPathSelected;
        evt.value.color = pathIndicator.path.cssColorStr;
        this.world3d.scene.dispatchEvent(evt);
        // this.world3d.outlineHelper.setOutlineThickness(3);
        this.world3d.outlineHelper.selectSingle(pathIndicator);
        const indices = [];
        let pointPos;
        intersects.forEach((intersect) => {
          if (intersect.object instanceof SprayPathIndicator) {
            indices.push(intersect.index);
            pointPos = intersect.point;
          }
        });
        const tipStr = pathIndicator.path.getTipStrByIndices(indices, this.world3d._meta.toEarchCoordMat);
        showSprayPointTip(this.world3d, pointPos, tipStr);
      }
    }
    return this._intersectObj;
  }
  _mousedown4UsingPlaneRef() {
    const raycaster = this.world3d.mouseRaycaster;
    if (!this.usingPlaneRef) {
      if (this.stateAddingArea || this.stateAddingHole) {
        const intersects = raycaster.intersectObjects(
          this.planeRefMeshes,
          false,
        );
        if (!intersects.length) {
          return false;
        }
        this.usingPlaneRef = intersects[0].object;
        this.usingPlaneRef.toggleSelected(true);
      }
    }
    if (this.stateAddingArea || this.stateAddingHole) {
      facing.updateByPlaneRef(
        this.world3d.camera,
        this.usingPlaneRef,
        this._alignToViewport,
      );
      rectOnPlane.onMouseDown(raycaster.ray, facing.plane, facing.planeMatInv);
      return true;
    }
    return false;
  }
  // 清除 各种对象的选中状态
  clearSelection() {
    if (this.pickingSprayPathIndicator()) {
      // this.world3d.outlineHelper.unSelect()
    }
    this.world3d.allPluginsDetach()
    this._intersectObj = null
    this._intersection = null
    this._usingFixedEye = null
  }

  resetPicking(obj) {
    this._intersectObj = obj
  }

  // 调用时机：鼠标移动时，正在新增工作区 或 不可喷涂区
  updateRectOnPlane() {
    const raycaster = this.world3d.mouseRaycaster
    rectOnPlane.onMouseMove(raycaster.ray, facing.plane, facing.planeMatInv, facing.planeMat)
    return rectOnPlane
  }

  getIntersection() {
    return this._intersection
  }

  /**
   * @return {WorkAreaMesh|null}
   */
  pickingArea() {
    if (this.areaMeshes.indexOf(this._intersectObj) >= 0) {
      return this._intersectObj
    }
    return null
  }

  /**
   * @return {PlaneRefMesh|null}
   */
  pickingPlaneRef() {
    if (this.planeRefMeshes.indexOf(this._intersectObj) >= 0) {
      return this._intersectObj
    }
    return null
  }

  /**
   * @return {ConvexAreaMesh|null}
   */
  pickingSurfaceMesh() {
    if (this.surfacesAreaMeshes.indexOf(this._intersectObj) >= 0) {
      return this._intersectObj
    }
    return null
  }

  /**
   * @return {RoutePointIndicator|null}
   */
  pickingRoutePoint() {
    return this.world3d._addRoutePointPlugin._selectedIndicator
  }

  /**
   * @return {HoleMesh|null}
   */
  pickingHole() {
    if (this.holeMeshes.indexOf(this._intersectObj) >= 0) {
      return this._intersectObj
    }
    return null
  }

  /**
   * @return {EyeIndicator|null}
   */
  pickingEyeIndicator() {
    if (this._intersectObj) {
      return this.fixedEyeIndicators.find(it => it.eyeMesh === this._intersectObj)
    }
    return null
  }

  /**
   * @return {SprayPathIndicator|null}
   */
  pickingSprayPathIndicator() {
    if (this.sprayPathIndicators.indexOf(this._intersectObj) >= 0) {
      return this._intersectObj
    }
    return null
  }

  /**
   * @return {b3dms 中的一个网格瓦片|null}
   */
  pickingTile() {
    const eyeMeshes = this.fixedEyeIndicators.filter(it => it.visible).map(it => it.eyeMesh)
    const meshes = this.areaMeshes.concat(this.holeMeshes).concat(eyeMeshes).concat(this.sprayPathIndicators)
    if (meshes.indexOf(this._intersectObj) < 0) {
      return this._intersectObj
    }
    return null
  }
  /**
   * @return {RouteVO|null}
   */
  pickRoute(routePointIndicator) {
    const pickPoint = routePointIndicator || this.pickingRoutePoint()
    const arr = this.getRouteList()
    const route = arr.find(it => it.pointMeshes.includes(pickPoint))
    if (!route) {
      return null
    }
    this.unpickRoute()
    this.pickingRoute = route
    route.pointMeshes.forEach(it => it.toggleSelected(true))
    return route
  }
  unpickRoute() {
    if (this.pickingRoute) {
      this.pickingRoute.pointMeshes.forEach(it => it.toggleSelected(false))
      this.pickingRoute = null
    }
  }
  getRouteList() {
    let arr = this.routeList.slice()
    if (this.currentRoute && !arr.includes(this.currentRoute)) {
      arr = arr.concat(this.currentRoute)
    }
    return arr
  }
  setCurrentRoute(routeVO) {
    if (this.currentRoute) {
      if (!this.routeList.includes(this.currentRoute)) {
        this.routeList.push(this.currentRoute)
      }
    }
    if (routeVO) {
      this.currentRoute = routeVO
    } else {
      const arr = this.getRouteList();
      const route = arr.find((it) =>
        it.pointMeshes.includes(this.pickingRoutePoint()),
      );
      this.currentRoute = route;
    }
  }

  /**
   * 通过UI交互来设置当前使用的固定视角，不是通过选中画布（3d场景）中的EyeIndicator  按 F来使用视角
   * @param {EyeIndicator} fixedEye
   */
  setUsingFixedEye(fixedEye) {
    this._usingFixedEye = fixedEye
  }
  getUsingFixedEye() {
    if (this._usingFixedEye) {
      return this._usingFixedEye
    }
    return this.pickingEyeIndicator()
  }

  // 调用时机：选中视角 或 工作区 或 孔洞 或 航线，按快捷键 F 聚焦
  focusTarget() {
    const areaOrHole = this.pickingArea() || this.pickingHole()
    const fixedEye = this.pickingEyeIndicator()
    const sprayPathIndicator = this.pickingSprayPathIndicator()
    const planeRefMesh = this.pickingPlaneRef()
    if (this.usingPlaneRef) {
      this.usingPlaneRef.toggleSelected(false);
      this.usingPlaneRef.toggleActive(false);
      this.usingPlaneRef = null;
    }
    if (areaOrHole) { // 使相机朝向选中的 工作区 或 孔洞
      const [focus, eyeFacing] = areaOrHole.vo.getEyeInfo()
      this.world3d.onCameraTargetChange(eyeFacing, focus, WorldConfig.eye_to_face_distance)
    } else if (fixedEye) { // 使用固定视角
      this._usingFixedEye = fixedEye
      this.world3d.editOrientation.useEyeIndicator()
    } else if (sprayPathIndicator) { // 聚焦到航线
      const sprayPath = sprayPathIndicator.path
      const eyeFacing = new THREE.Vector3().copy(sprayPath.facing).negate()
      const box = sprayPath.boundingBox
      const focus = box.getCenter(new THREE.Vector3())
      const size = box.getSize(new THREE.Vector3())
      const distance = Math.max(size.x, size.y, size.z)
      this.world3d.onCameraTargetChange(eyeFacing, focus, distance)
    } else if (planeRefMesh) { // 聚焦到辅助平面
      this.usingPlaneRef = planeRefMesh;
      planeRefMesh.toggleSelected(true);
      planeRefMesh.toggleActive(true);
      const focus = planeRefMesh.position;
      const eyeFacing = planeRefMesh.vo.plane.normal.clone().negate();
      this.world3d.onCameraTargetChange(eyeFacing, focus, WorldConfig.eye_to_face_distance);
    } else {
      this.focusDroneMesh()
    }
  }

  // 创建并初始化新的工作区
  initNewAreaMesh() {
    const geo = new THREE.BufferGeometry()
    const fVector = facing.vector
    geo.setAttribute( 'position', new THREE.Float32BufferAttribute( [0,0,0, 0,0,0, 0,0,0, 0,0,0], 3 ) )
    geo.setAttribute( 'normal', new THREE.Float32BufferAttribute( flatten([fVector, fVector, fVector, fVector]), 3 ) )
    geo.setAttribute( 'uv', new THREE.Float32BufferAttribute( [0,0, 0,0, 0,0, 0,0], 2 ) )
    geo.setIndex([0, 1, 2, 2, 3, 0])

    this.addingAreaMesh = new WorkAreaMesh(geo, areaMtl.clone())
    this.addingAreaMesh.renderOrder = WorldConfig.order_work_area
    this.world3d.scene.add(this.addingAreaMesh)
    const {vector, planeMat, planeMatInv} = facing
    const workArea = new WorkArea(vector, planeMat, planeMatInv)
    this.addingAreaMesh.vo = workArea
  }

  // 鼠标拖动时 改变新增工作区的矩形大小
  updateAddingAreaRect() {
    const rectOnPlane = this.updateRectOnPlane()
    // 正在画矩形 新增工作区
    const posAttr = this.addingAreaMesh.geometry.attributes.position
    posAttr.copyVector3sArray(rectOnPlane.vectors)
    posAttr.needsUpdate = true
    this.addingAreaMesh.geometry.computeBoundingSphere() // 非 +z 轴，必须调用computeBoundingSphere；才有渐变过程
    const uvAttr = this.addingAreaMesh.geometry.attributes.uv
    uvAttr.copyVector2sArray(rectOnPlane.uvs)
    uvAttr.needsUpdate = true
    this.addingAreaMesh.vo.area = rectOnPlane.area
    showAddingWorkAreaTip(this.world3d, rectOnPlane)
  }

  // 取消 正在新增的工作区网格
  clearAddingAreaMesh() {
    if (this.addingAreaMesh) {
      this.addingAreaMesh.removeFromParent()
      this.addingAreaMesh = null
    }
  }

  // 正在新增的工作区网格 加入到数据中心 成功新增一个工作区
  addAreaMeshToStore() {
    this.areaMeshes.push(this.addingAreaMesh)
    this.addingAreaMesh = null
  }

  // 创建并初始化新的不可喷涂区域
  initNewHoleMesh() {
    const fVector = facing.vector
    const geo = new THREE.BufferGeometry()
    geo.setAttribute( 'position', new THREE.Float32BufferAttribute( [0,0,0, 0,0,0, 0,0,0, 0,0,0], 3 ) )
    geo.setAttribute( 'normal', new THREE.Float32BufferAttribute( flatten([fVector, fVector, fVector, fVector]), 3 ) )
    geo.setAttribute( 'uv', new THREE.Float32BufferAttribute( [0,0, 0,0, 0,0, 0,0], 2 ) )
    geo.setIndex([0, 1, 2, 2, 3, 0])
    this.addingHoleMesh = new HoleMesh(geo, holeMtl)
    holeMtl.depthTest = false
    this.addingHoleMesh.renderOrder = WorldConfig.order_hole
    this.world3d.scene.add(this.addingHoleMesh)
    const holeArea = new WorkArea(fVector, facing.planeMat, facing.planeMatInv)
    holeArea.isHole = true
    this.addingHoleMesh.vo = holeArea
  }

  // 鼠标拖动时 改变孔洞的矩形大小
  updateAddingHoleRect() {
    const rectOnPlane = this.updateRectOnPlane()
    const posAttr = this.addingHoleMesh.geometry.attributes.position
    posAttr.copyVector3sArray(rectOnPlane.vectors)
    this.addingHoleMesh.vo.area = rectOnPlane.area
    posAttr.needsUpdate = true
    this.addingHoleMesh.geometry.computeBoundingSphere()
  }

  // 取消 正在新增的不可喷涂区域网格
  clearAddingHoleMesh() {
    if (this.addingHoleMesh) {
      this.addingHoleMesh.removeFromParent()
      this.addingHoleMesh = null
    }
  }

  // 正在新增的孔洞网格 加入到数据中心 成功新增一个不可喷涂区域
  addHoleMeshToStore() {
    this.addingHoleMesh.geometry.computeBoundingBox()
    this.addingHoleMesh.geometry.computeBoundingSphere()
    this.holeMeshes.push(this.addingHoleMesh)
    holeMtl.depthTest = true
    this.addingHoleMesh = null
  }
  setRouteSpeed(speed) {
    if (!this.pickingRoute) {
      return;
    }
    this.pickingRoute.editSpeed(speed)
  }
  setRouteColor(cssColor) {
    if (!this.pickingRoute) {
      return;
    }
    const colorCode = mapColorCssToCode.get(cssColor)
    this.pickingRoute.changeColorCode(colorCode)
  }
  setRouteSprayDistance(distance) {
    if (!this.pickingRoute) {
      return;
    }
    this.world3d._addRoutePointPlugin.updateRouteHorizontalOffset(distance);
    this.pickingRoute.editSprayDistance(distance)
  }
  setRouteAllPointRadius(radius) {
    if (!this.pickingRoute) {
      return;
    }
    this.pickingRoute.updateRoutePointRadius(radius)
  }

  // 删除航线校准点 或 航线起飞点
  removeCurrentHoverPoint() {
    if (!this.curHoverPoint) {
      return;
    }
    this.curHoverPoint.removeFromParent();
    let index = this.calibrationPoints.indexOf(this.curHoverPoint);
    if (index >= 0) {
      this.calibrationPoints.splice(index, 1);
      this.world3d.refreshCalibrationPoints()
    }
    index = this.flyHomePoints.indexOf(this.curHoverPoint);
    if (index >= 0) {
      this.flyHomePoints.splice(index, 1);
    }
  }

  removeSprayPath() {
    const pathIndicator = this.pickingSprayPathIndicator()
    if (!pathIndicator) {
      return
    }
    this.world3d.scene.dispatchEvent(World3dEvent.clearPaneSelection)

    const index = this.sprayPathIndicators.indexOf(pathIndicator)
    this.sprayPathIndicators.splice(index, 1)
    pathIndicator.removeFromParent()
    this.resetPicking(null)
    // 删除与航线关联的所有工作区
    // const areaIds = pathIndicator.path.workAreaIds
    // if (areaIds) {
    //   areaIds.forEach(areaId => {
    //     const areaMesh = this.areaMeshes.find(it => it.vo.id === areaId)
    //     if (areaMesh) {
    //       const index = this.areaMeshes.indexOf(areaMesh)
    //       this.areaMeshes.splice(index, 1)
    //       areaMesh.removeFromParent()
    //     }
    //   })
    // }
  }

  removeBlocking() {
    const blockingMesh =
      this.world3d.transformRotateControl.object ||
      this.world3d.scaleControl.object;
    if (!(blockingMesh instanceof BlockingMesh)) {
      return;
    }
    this.world3d.transformRotateControl.detach();
    this.world3d.scaleControl.detach();
    blockingMesh.removeFromParent();
    removeArrayItem(this.blockings, blockingMesh);
    const ctx = {
      blockingMesh: blockingMesh,
      arr: this.world3d.store.blockings,
      scene: this.world3d.scene,
    };
    this.world3d.editRecord.addAction(UndoType.removeBlocking, ctx);
  }

  removePlaneRef() {
    const planeRefMesh = this.pickingPlaneRef();
    if (!planeRefMesh) {
      return;
    }
    if (planeRefMesh === this.usingPlaneRef) {
      return; // 不允许删除激活的辅助平面
      // this.usingPlaneRef = null;
    }
    removeArrayItem(this.planeRefMeshes, planeRefMesh);
    planeRefMesh.removeFromParent();
  }
  removeSurfaceMesh() {
    const surfaceMesh = this.pickingSurfaceMesh();
    if (!surfaceMesh) {
      return;
    }
    removeArrayItem(this.surfacesAreaMeshes, surfaceMesh);
    surfaceMesh.removeFromParent();
    this.world3d.editOrientation.shapeExtractAdhere.onAreaAdded();
  }
  // 删除整条航线
  removePickingRoute() {
    if (!this.pickingRoute) {
      return
    }
    arrAsubB(this.routePoints, this.pickingRoute.pointMeshes)
    this.pickingRoute.pointMeshes.forEach(it => {
      it.disposeLine()
      it.removeFromParent()
    })
    removeArrayItem(this.routeList, this.pickingRoute)
    if (this.currentRoute === this.pickingRoute) {
      this.currentRoute = null
    }
    this.world3d._addRoutePointPlugin._unSelect()
    const ctx = {
      scene: this.world3d.scene,
      route: this.pickingRoute,
      routeArr: this.routeList,
      routePoints: this.routePoints,
    }
    this.world3d.editRecord.addAction(UndoType.removeRoute, ctx);
    this.pickingRoute = null
  }
  // 删除单个航点
  removeRoutePoint() {
    const routePoint = this.pickingRoutePoint()
    if (!routePoint) {
      return;
    }
    const arr = this.getRouteList()
    const route = arr.find(it => it.pointMeshes.includes(this.pickingRoutePoint()))
    // 注意：removePickingRoute 调用后，紧接着调用 removeRoutePoint，这里的 route 会为 undefined
    const points = route.pointMeshes
    const index = points.indexOf(routePoint)
    if (index < points.length - 1) {
      const pointNext = points[index + 1]
      const pointPrev = points[index - 1]
      if (pointNext) {
        if (pointPrev) {
          pointNext.updateLine(pointPrev.vo)
        } else {
          pointNext.disposeLine()
        }
      }
    }
    route.removePointMesh(routePoint)
    if (!route.pointMeshes.length) {
      removeArrayItem(this.routeList, route)
      if (this.currentRoute === route) {
        this.currentRoute = null
      }
    }
    removeArrayItem(this.routePoints, routePoint)
    routePoint.disposeLine()
    routePoint.removeFromParent()
    const ctx = {
      routePointIndicator: routePoint,
      arr: this.routePoints,
      sequence: points,
      scene: this.world3d.scene,
      route,
      routeArr: this.routeList,
      idx: index,
    }
    this.world3d.editRecord.addAction(UndoType.removeRoutePoint, ctx);
  }

  // 切换所有航线指示器的可见性
  toggleVisibleSprayPaths() {
    toggleVisibility(this.sprayPathIndicators);
  }

  // 切换所有固定视角指示器的可见性
  toggleVisibleEyeIndicators() {
    eyeIndicatorsHided = !eyeIndicatorsHided;
    toggleVisibility(this.fixedEyeIndicators);
  }

  // 切换所有工作区和不可喷涂区域的可见性
  toggleVisibleAreas() {
    const arr = this.areaMeshes.concat(this.holeMeshes);
    toggleVisibility(arr);
  }

  fixedEyesIsHidden() {
    return eyeIndicatorsHided;
  }

  get stateSettingFocus() {
    return state === stateSettingFocus;
  }

  get stateAddingArea() {
    return state === stateAddingArea;
  }

  get stateAddingHole() {
    return state === stateAddingHole;
  }

  get stateEditingHole() {
    return state === stateEditingHole;
  }

  get stateAddingFixedEye() {
    return state === stateAddingFixedEye;
  }

  get stateAddingSampleCubeVert() {
    return state === stateAddingSampleCubeVert;
  }
  get stateAddingConvexVert() {
    return state === stateAddingConvexVert
  }
  get stateAddingGridVert() {
    return state === stateAddingGridVert
  }
  get stateAddingSurfaceArea() {
    return state === stateAddingSurfaceArea
  }

  get stateAddingBlocking() {
    return state === stateAddingBlocking;
  }

  get stateEditingSprayPath() {
    return state === stateEditingSprayPath;
  }

  get stateMeasurePoint() {
    return state === stateMeasurePoint;
  }

  get stateSettingCalibrationPoint() {
    return state === stateSettingCalibrationPoint;
  }

  get stateSettingHomePoint() {
    return state === stateSettingHomePoint;
  }

  get statePickLngLat() {
    return state === statePickLngLat;
  }

  get stateMovingStartEnd() {
    return state === stateMovingStartEnd;
  }

  get stateAddingPlaneRef() {
    return state === stateAddingPlaneRef;
  }

  get stateAddingSliceLine() {
    return state === stateAddingSliceLine;
  }

  get stateAddingRoutePoint() {
    return state === stateAddingRoutePoint
  }
  get stateMovingRoutePoint() {
    return state === stateMovingRoutePoint
  }
  get stateAddingNormalPoint() {
    return state === stateAddingNormalPoint
  }
  get stateMovingNormalPoint() {
    return state === stateMovingNormalPoint
  }

  get stateFree() {
    return state === stateFree
  }

  freeState() {
    state = stateFree
    this.world3d.scene.dispatchEvent(World3dEvent.clearEditorState)
  }

  editingHole() {
    state = stateEditingHole
  }

  addingHole() {
    state = stateAddingHole;
  }

  addingWorkArea() {
    state = stateAddingArea;
  }

  addingFixedEye() {
    state = stateAddingFixedEye;
  }

  settingSampleCubeVert() {
    state = stateAddingSampleCubeVert;
  }
  addingConvexVert() {
    state = stateAddingConvexVert
  }
  addingGridVert() {
    state = stateAddingGridVert
  }
  addingSurfaceArea() {
    state = stateAddingSurfaceArea
  }

  addingBlocking() {
    state = stateAddingBlocking;
  }

  settingFocus() {
    state = stateSettingFocus;
  }

  settingSprayPath() {
    state = stateEditingSprayPath;
  }

  settingMeasurePoint() {
    state = stateMeasurePoint;
  }

  settingMoveStartEnd() {
    state = stateMovingStartEnd;
  }

  settingCalibrationPoint() {
    state = stateSettingCalibrationPoint;
  }

  settingHomePoint() {
    state = stateSettingHomePoint;
  }

  settingPickLngLat() {
    state = statePickLngLat;
  }

  settingPlaneRef() {
    state = stateAddingPlaneRef;
  }

  addingSliceLine() {
    state = stateAddingSliceLine;
  }

  addingRoutePoints() {
    state = stateAddingRoutePoint
  }

  movingRoutePoint() {
    state = stateMovingRoutePoint
  }

  addingNormalPoints() {
    state = stateAddingNormalPoint
  }
  movingNormalPoint() {
    state = stateMovingNormalPoint
  }
}
